#include <QtCore/QString>
#include <QtTest/QtTest>
#include <QDebug>
#include <stdlib.h>
#include <botan/botan.h>

#include "SymmetricEncrypter.h"
#include "SymmetricKeyFile.h"
#include "SymmetricKeyMemory.h"

using namespace kryptos::cryptography;

class SymmetricEncryptionTest : public QObject
{
    Q_OBJECT

public:
    SymmetricEncryptionTest(){}

private Q_SLOTS:

    void initTestCase();
    void cleanupTestCase();

    void readSymmetircKeyInMemory();
    void readReusableSymmetircKeyInMemory();
    void readSymmetircKeyFromFile();
    void readReusableSymmetircKeyFromFile();

    void encryptBuffer();
    void decryptBuffer();
    void encryptFile();
    void decryptFile();
    void encryptWithStreams();
    void decryptWithStreams();

private:

    void testKey(SymmetricKey& key);
    void testReusableKey(SymmetricKey& key);

    SymmetricKeyMemory keyMemory, keyMemoryReusable;
    SymmetricKeyFile   keyFile, keyFileLarge, keyFileReusable;

    std::vector<char> encrypted, decrypted, original;
};

void SymmetricEncryptionTest::initTestCase()
{
    keyMemory.change("ABCDEFGHIJKLMNOPQRSTUVWXYZ012345", 32);
    keyMemoryReusable.change("ABCDEFGHIJKLMNOPQRSTUVWXYZ012345", 32, true);
    keyFile.change("key.txt");
    keyFileReusable.change("key.txt", true);
    keyFileLarge.change("keylarge.txt");

    original.resize(32);
    memcpy(&original[0], "PROBANDO TEXTO DE 32 BYTES!!!!!!", 32);
}

void SymmetricEncryptionTest::cleanupTestCase()
{
}

void SymmetricEncryptionTest::testReusableKey(SymmetricKey& key)
{
    std::vector<char> bytes = key.nextBytes(20);

    QVERIFY2(!bytes.empty(), "Key read cannot be not empty, it didnt reach the end");
    QCOMPARE(std::string(&bytes.front(), 20).c_str(), "ABCDEFGHIJKLMNOPQRST");

    bytes.clear();
    bytes = key.nextBytes(16);

    QVERIFY2(!bytes.empty(), "Key read cannot be not empty, it didnt reach the end");
    QCOMPARE(std::string(&bytes.front(), 16).c_str(), "UVWXYZ012345ABCD");

    bytes.clear();
    bytes = key.nextBytes(10);

    QVERIFY2(!bytes.empty(), "Key read cannot be not empty, it didnt reach the end");
    QCOMPARE(std::string(&bytes.front(), 10).c_str(), "EFGHIJKLMN");

    bytes.clear();
    key.goBack(26);

    bytes = key.nextBytes(16);

    QVERIFY2(!bytes.empty(), "Key read cannot be not empty, it didnt reach the end");
    QCOMPARE(std::string(&bytes.front(), 16).c_str(), "UVWXYZ012345ABCD");
}

void SymmetricEncryptionTest::testKey(SymmetricKey& key)
{
    std::vector<char> bytes = key.nextBytes(16);

    QVERIFY2(!bytes.empty(), "Key read cannot be not empty, it didnt reach the end");
    QCOMPARE(std::string(&bytes.front(), 16).c_str(), "ABCDEFGHIJKLMNOP");

    bytes.clear();
    bytes = key.nextBytes(10);

    QVERIFY2(!bytes.empty(), "Key read cannot be not empty, it didnt reach the end");
    QCOMPARE(std::string(&bytes.front(), 10).c_str(), "QRSTUVWXYZ");

    bytes.clear();
    bytes = key.nextBytes(10);

    QVERIFY2(bytes.empty(), "Should be empty because there was more bytes requested than the ones left in the key");

    bytes.clear();
    key.goBack(10);

    bytes = key.nextBytes(10);

    QVERIFY2(!bytes.empty(), "Key read cannot be not empty, it didnt reach the end");
    QCOMPARE(std::string(&bytes.front(), 10).c_str(), "QRSTUVWXYZ");
}

void SymmetricEncryptionTest::readSymmetircKeyInMemory()
{
    testKey(keyMemory);
}

void SymmetricEncryptionTest::readReusableSymmetircKeyInMemory()
{
    testReusableKey(keyMemoryReusable);
}

void SymmetricEncryptionTest::readSymmetircKeyFromFile()
{
    testKey(keyFile);
}

void SymmetricEncryptionTest::readReusableSymmetircKeyFromFile()
{
    testReusableKey(keyFileReusable);
}

void SymmetricEncryptionTest::encryptBuffer()
{
    keyMemory.reset();
    SymmetricEncrypter encrypter(keyMemory);

    encrypter.encryptBuffer(original.data(), original.size(), encrypted);
}

void SymmetricEncryptionTest::decryptBuffer()
{
    keyMemory.reset();
    SymmetricEncrypter encrypter(keyMemory);
    encrypter.decryptBuffer(encrypted.data(), encrypted.size(), decrypted);

    QVERIFY((decrypted == original));
}

void SymmetricEncryptionTest::encryptFile()
{
    QFile::remove("encfile.krp");

    keyFileReusable.reset();

    SymmetricEncrypter encrypter(keyFileReusable);
    encrypter.encryptFile("testfile.txt", "encfile.krp");
}

void SymmetricEncryptionTest::decryptFile()
{
    QFile::remove("decryptedfile.txt");

    keyFileReusable.reset();
    SymmetricEncrypter encrypter(keyFileReusable);
    encrypter.decryptFile("encfile.krp", "decryptedfile.txt");
}

void SymmetricEncryptionTest::encryptWithStreams()
{
    QFile::remove("encfile_streams.krp");

    keyFileReusable.reset();
    SymmetricEncrypter encrypter(keyFileReusable);

    std::ofstream output("encfile_streams.krp", std::ios::binary);
    encrypter.encryptFile("testfile.txt", output);
    output.close();
}

void SymmetricEncryptionTest::decryptWithStreams()
{
    QFile::remove("decryptedfile_streams.txt");

    keyFileReusable.reset();
    SymmetricEncrypter encrypter(keyFileReusable);

    std::ifstream input("encfile_streams.krp", std::ios::binary);
    std::ofstream decoutput("decryptedfile_streams.txt", std::ios::binary);
    encrypter.decryptFile(input, decoutput);
    decoutput.close();
}


QTEST_MAIN(SymmetricEncryptionTest);

#include "SymmetricEncryptionTest.moc"
